using System; 
using System.Collections.Generic;
using System.Data;
using System.Data.Linq;
using System.Data.Linq.Mapping; 
using System.Globalization;
 
namespace LinqToSqlShared.Mapping { 
    /// <summary>
    /// DatabaseMapping and related classes represent a parsed version of the 
    /// XML mapping string. This unvalidated intermediate representation is
    /// necessary because unused mappings are intentially never validated.
    /// </summary>
    internal class DatabaseMapping { 
        string databaseName;
        string provider; 
        List<TableMapping> tables; 
        List<FunctionMapping> functions;
 
        internal DatabaseMapping() {
            this.tables = new List<TableMapping>();
            this.functions = new List<FunctionMapping>();
        } 

        internal string DatabaseName { 
            get { return this.databaseName; } 
            set { this.databaseName = value; }
        } 

        internal string Provider {
            get { return this.provider; }
            set { this.provider = value; } 
        }
 
        internal List<TableMapping> Tables { 
            get { return this.tables; }
        } 

        internal List<FunctionMapping> Functions {
            get { return this.functions; }
        } 

        internal TableMapping GetTable(string tableName) { 
            foreach (TableMapping tmap in this.tables) { 
                if (string.Compare(tmap.TableName, tableName, StringComparison.Ordinal) == 0)
                    return tmap; 
            }
            return null;
        }
 
        internal TableMapping GetTable(Type rowType) {
            foreach (TableMapping tableMap in this.tables) { 
                if (this.IsType(tableMap.RowType, rowType)) { 
                    return tableMap;
                } 
            }
            return null;
        }
 
        private bool IsType(TypeMapping map, Type type) {
            if (string.Compare(map.Name, type.Name, StringComparison.Ordinal) == 0 
                || string.Compare(map.Name, type.FullName, StringComparison.Ordinal) == 0 
                || string.Compare(map.Name, type.AssemblyQualifiedName, StringComparison.Ordinal) == 0)
                return true; 
            foreach (TypeMapping subMap in map.DerivedTypes) {
                if (this.IsType(subMap, type))
                    return true;
            } 
            return false;
        } 
 
        internal FunctionMapping GetFunction(string functionName) {
            foreach (FunctionMapping fmap in this.functions) { 
                if (string.Compare(fmap.Name, functionName, StringComparison.Ordinal) == 0)
                    return fmap;
            }
            return null; 
        }
    } 
 
    /// <summary>
    /// Constants in the mapping schema. 
    /// </summary>
    class XmlMappingConstant {
        internal const string Association = "Association";
        internal const string AutoSync = "AutoSync"; 
        internal const string Column = "Column";
        internal const string Database = "Database"; 
        internal const string DbType = "DbType"; 
        internal const string DeleteRule = "DeleteRule";
        internal const string DeleteOnNull = "DeleteOnNull"; 
        internal const string Direction = "Direction";
        internal const string ElementType = "ElementType";
        internal const string Expression = "Expression";
        internal const string False = "false"; 
        internal const string Function = "Function";
        internal const string InheritanceCode = "InheritanceCode"; 
        internal const string IsComposable = "IsComposable"; 
        internal const string IsDbGenerated = "IsDbGenerated";
        internal const string IsDiscriminator = "IsDiscriminator"; 
        internal const string IsPrimaryKey = "IsPrimaryKey";
        internal const string IsInheritanceDefault = "IsInheritanceDefault";
        internal const string IsForeignKey = "IsForeignKey";
        internal const string IsUnique = "IsUnique"; 
        internal const string IsVersion = "IsVersion";
        internal const string MappingNamespace = "http://schemas.microsoft.com/linqtosql/mapping/2007"; 
        internal const string Member = "Member"; 
        internal const string Method = "Method";
        internal const string Name = "Name"; 
        internal const string CanBeNull = "CanBeNull";
        internal const string OtherKey = "OtherKey";
        internal const string Parameter = "Parameter";
        internal const string Provider = "Provider"; 
        internal const string Return = "Return";
        internal const string Storage = "Storage"; 
        internal const string Table = "Table"; 
        internal const string ThisKey = "ThisKey";
        internal const string True = "true"; 
        internal const string Type = "Type";
        internal const string UpdateCheck = "UpdateCheck";
    }
 
    internal class TableMapping {
        string tableName; 
        string member; 
        TypeMapping rowType;
 
        internal TableMapping() {
        }

        internal string TableName { 
            get { return this.tableName; }
            set { this.tableName = value; } 
        } 

        internal string Member { 
            get { return this.member; }
            set { this.member = value; }
        }
 
        internal TypeMapping RowType {
            get { return this.rowType; } 
            set { this.rowType = value; } 
        }
    } 

    internal class FunctionMapping {
        string name;
        string methodName; 
        bool isComposable;
        List<ParameterMapping> parameters; 
        List<TypeMapping> types; 
        ReturnMapping funReturn;
 
        internal FunctionMapping() {
            this.parameters = new List<ParameterMapping>();
            this.types = new List<TypeMapping>();
        } 

        internal string Name { 
            get { return this.name; } 
            set { this.name = value; }
        } 

        internal string MethodName {
            get { return this.methodName; }
            set { this.methodName = value; } 
        }
 
        internal bool IsComposable { 
            get { return this.isComposable; }
            set { this.isComposable = value; } 
        }

        internal string XmlIsComposable {
            get { return this.isComposable ? XmlMappingConstant.True : null; } 
            set { this.isComposable = (value != null) ? bool.Parse(value) : false; }
        } 
 
        internal List<ParameterMapping> Parameters {
            get { return this.parameters; } 
        }

        internal List<TypeMapping> Types {
            get { return this.types; } 
        }
 
        internal ReturnMapping FunReturn { 
            get { return this.funReturn; }
            set { this.funReturn = value; } 
        }
    }

    internal enum MappingParameterDirection { 
        In,
        Out, 
        InOut 
    }
 
    internal class ParameterMapping {
        string name;
        string parameterName;
        string dbType; 
        MappingParameterDirection direction;
 
        internal string Name { 
            get { return this.name; }
            set { this.name = value; } 
        }

        internal string ParameterName {
            get { return this.parameterName; } 
            set { this.parameterName = value; }
        } 
 
        internal string DbType {
            get { return this.dbType; } 
            set { this.dbType = value; }
        }

        public string XmlDirection { 
            get { return this.direction == MappingParameterDirection.In ? null : this.direction.ToString(); }
            set { 
                this.direction = (value == null) 
                    ? MappingParameterDirection.In
                    : (MappingParameterDirection)Enum.Parse(typeof(MappingParameterDirection), value, true); 
            }
        }

        public MappingParameterDirection Direction { 
            get { return this.direction; }
            set { this.direction = value; } 
        } 
    }
 
    internal class ReturnMapping {
        string dbType;

        internal string DbType { 
            get { return this.dbType; }
            set { this.dbType = value; } 
        } 
    }
 

    internal class TypeMapping {
        string name;
        TypeMapping baseType; 
        List<MemberMapping> members;
        string inheritanceCode; 
        bool isInheritanceDefault; 
        List<TypeMapping> derivedTypes;
 
        internal TypeMapping() {
            this.members = new List<MemberMapping>();
            this.derivedTypes = new List<TypeMapping>();
        } 

        internal TypeMapping BaseType { 
            get { return this.baseType; } 
            set { this.baseType = value; }
        } 

        internal string Name {
            get { return this.name; }
            set { this.name = value; } 
        }
 
        internal List<MemberMapping> Members { 
            get { return this.members; }
        } 

        internal string InheritanceCode {
            get { return this.inheritanceCode; }
            set { this.inheritanceCode = value; } 
        }
 
        internal bool IsInheritanceDefault { 
            get { return this.isInheritanceDefault; }
            set { this.isInheritanceDefault = value; } 
        }

        internal string XmlIsInheritanceDefault {
            get { return this.isInheritanceDefault ? XmlMappingConstant.True : null; } 
            set { this.isInheritanceDefault = (value != null) ? bool.Parse(value) : false; }
        } 
 
        internal List<TypeMapping> DerivedTypes {
            get { return this.derivedTypes; } 
        }
    }

    internal abstract class MemberMapping { 
        string name;
        string member; 
        string storageMember; 

        internal MemberMapping() { 
        }

        internal string DbName {
            get { return this.name; } 
            set { this.name = value; }
        } 
 
        internal string MemberName {
            get { return this.member; } 
            set { this.member = value; }
        }

        internal string StorageMemberName { 
            get { return this.storageMember; }
            set { this.storageMember = value; } 
        } 
    }
 
    internal sealed class ColumnMapping : MemberMapping {
        string dbType;
        string expression;
        bool isPrimaryKey; 
        bool isDBGenerated;
        bool isVersion; 
        bool isDiscriminator; 
        bool? canBeNull = null;
        UpdateCheck updateCheck; 
        AutoSync autoSync;

        internal ColumnMapping() {
        } 

        internal string DbType { 
            get { return this.dbType; } 
            set { this.dbType = value; }
        } 

        internal bool? CanBeNull {
            get { return this.canBeNull; }
            set { this.canBeNull = value; } 
        }
 
        internal string XmlCanBeNull { 
            get {
                if (this.canBeNull == null) return null; 
                return this.canBeNull == true ? null : XmlMappingConstant.False;
            }
            set { this.canBeNull = (value != null) ? bool.Parse(value) : true; }
        } 

        internal string Expression { 
            get { return this.expression; } 
            set { this.expression = value; }
        } 

        internal bool IsPrimaryKey {
            get { return this.isPrimaryKey; }
            set { this.isPrimaryKey = value; } 
        }
 
        internal string XmlIsPrimaryKey { 
            get { return this.isPrimaryKey ? XmlMappingConstant.True : null; }
            set { this.isPrimaryKey = (value != null) ? bool.Parse(value) : false; } 
        }

        internal bool IsDbGenerated {
            get { return this.isDBGenerated; } 
            set { this.isDBGenerated = value; }
        } 
 
        internal string XmlIsDbGenerated {
            get { return this.isDBGenerated ? XmlMappingConstant.True : null; } 
            set { this.isDBGenerated = (value != null) ? bool.Parse(value) : false; }
        }

        internal bool IsVersion { 
            get { return this.isVersion; }
            set { this.isVersion = value; } 
        } 

        internal string XmlIsVersion { 
            get { return this.isVersion ? XmlMappingConstant.True : null; }
            set { this.isVersion = (value != null) ? bool.Parse(value) : false; }
        }
 
        internal bool IsDiscriminator {
            get { return this.isDiscriminator; } 
            set { this.isDiscriminator = value; } 
        }
 
        internal string XmlIsDiscriminator {
            get { return this.isDiscriminator ? XmlMappingConstant.True : null; }
            set { this.isDiscriminator = (value != null) ? bool.Parse(value) : false; }
        } 

        internal UpdateCheck UpdateCheck { 
            get { return this.updateCheck; } 
            set { this.updateCheck = value; }
        } 

        internal string XmlUpdateCheck {
            get { return this.updateCheck != UpdateCheck.Always ? this.updateCheck.ToString() : null; }
            set { this.updateCheck = (value == null) ? UpdateCheck.Always : (UpdateCheck)Enum.Parse(typeof(UpdateCheck), value); } 
        }
 
        internal AutoSync AutoSync { 
            get { return this.autoSync; }
            set { this.autoSync = value; } 
        }

        internal string XmlAutoSync {
            get { return this.autoSync != AutoSync.Default ? this.autoSync.ToString() : null; } 
            set { this.autoSync = (value != null) ? (AutoSync)Enum.Parse(typeof(AutoSync), value) : AutoSync.Default; }
        } 
    } 

    internal sealed class AssociationMapping : MemberMapping { 
        string thisKey;
        string otherKey;
        string deleteRule;
        bool deleteOnNull; 
        bool isForeignKey;
        bool isUnique; 
 
        internal AssociationMapping() {
        } 

        internal string ThisKey {
            get { return this.thisKey; }
            set { this.thisKey = value; } 
        }
 
        internal string OtherKey { 
            get { return this.otherKey; }
            set { this.otherKey = value; } 
        }

        internal string DeleteRule {
            get { return this.deleteRule; } 
            set { this.deleteRule = value; }
        } 
 
        internal bool DeleteOnNull {
            get { return this.deleteOnNull; } 
            set { this.deleteOnNull = value; }
        }

        internal bool IsForeignKey { 
            get { return this.isForeignKey; }
            set { this.isForeignKey = value; } 
        } 

        internal string XmlIsForeignKey { 
            get { return this.isForeignKey ? XmlMappingConstant.True : null; }
            set { this.isForeignKey = (value != null) ? bool.Parse(value) : false; }
        }
 
        internal string XmlDeleteOnNull {
            get { return this.deleteOnNull ? XmlMappingConstant.True : null; } 
            set { this.deleteOnNull = (value != null) ? bool.Parse(value) : false; } 
        }
 
        internal bool IsUnique {
            get { return this.isUnique; }
            set { this.isUnique = value; }
        } 

        internal string XmlIsUnique { 
            get { return this.isUnique ? XmlMappingConstant.True : null; } 
            set { this.isUnique = (value != null) ? bool.Parse(value) : false; }
        } 
    }

    /// <summary>
    /// Shared rules governing the mapping system. 
    /// </summary>
    internal static class MappingSystem { 
        /// <summary> 
        /// Return true if this is a clr type supported as an inheritance discriminator.
        /// </summary> 
        /// <param name="type"></param>
        /// <returns></returns>
        internal static bool IsSupportedDiscriminatorType(Type type) {
            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) { 
                type = type.GetGenericArguments()[0];
            } 
            switch (Type.GetTypeCode(type)) { 
                case TypeCode.Byte:
                case TypeCode.SByte: 
                case TypeCode.Int16:
                case TypeCode.Int32:
                case TypeCode.Int64:
                case TypeCode.UInt16: 
                case TypeCode.UInt32:
                case TypeCode.UInt64: 
                case TypeCode.Char: 
                case TypeCode.String:
                case TypeCode.Boolean: 
                    return true;
            }
            return false;
        } 

        /// <summary> 
        /// Return true if this is a clr type supported as an inheritance discriminator. 
        /// </summary>
        /// <param name="type"></param> 
        /// <returns></returns>
        internal static bool IsSupportedDiscriminatorType(SqlDbType type) {
            switch (type) {
                case SqlDbType.BigInt: 
                case SqlDbType.Bit:
                case SqlDbType.Char: 
                case SqlDbType.Int: 
                case SqlDbType.NChar:
                case SqlDbType.NVarChar: 
                case SqlDbType.SmallInt:
                case SqlDbType.TinyInt:
                case SqlDbType.VarChar:
                    return true; 
            }
            return false; 
        } 

        /// <summary> 
        /// Return true if this is a CLR type supported as an identity member.  Since identity
        /// management (caching) relies on key members being hashable, only types implementing
        /// GetHashCode are supported.  Also, the runtime relies on identity members being comparable,
        /// so only types implementing Equals are supported. 
        /// </summary>
        internal static bool IsSupportedIdentityType(Type type) 
        { 
            if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
            { 
                type = type.GetGenericArguments()[0];
            }
            if (type == typeof(Guid) || type == typeof(DateTime) || type == typeof(DateTimeOffset) ||
                type == typeof(TimeSpan) || type == typeof(Binary)) 
            {
                return true; 
            } 
            switch (Type.GetTypeCode(type))
            { 
                case TypeCode.Byte:
                case TypeCode.SByte:
                case TypeCode.Int16:
                case TypeCode.Int32: 
                case TypeCode.Int64:
                case TypeCode.UInt16: 
                case TypeCode.UInt32: 
                case TypeCode.UInt64:
                case TypeCode.Char: 
                case TypeCode.String:
                case TypeCode.Boolean:
                case TypeCode.Decimal:
                case TypeCode.Single: 
                case TypeCode.Double:
                    return true; 
            } 
            return false;
        } 

        /// <summary>
        /// Types that do not support comparison cannot be used as primary keys.  The
        /// database will restrict this, but we can't rely on that, since it is possible 
        /// to create a key mapping to a column that isn't truly a key in the DB.
        /// </summary> 
        internal static bool IsSupportedIdentityType(SqlDbType type) 
        {
            switch (type) { 
                case SqlDbType.NText:
                case SqlDbType.Image:
                case SqlDbType.Xml:
                case SqlDbType.Text: 
                case SqlDbType.Variant:
                case SqlDbType.Udt: 
                    return false; 
                default:
                    return true; 
            }
        }

    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.